package cn.hnedu.util;

import org.apache.struts.util.MessageResources;
import org.apache.struts.util.MessageResourcesFactory;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class EncodeMessageResources
    extends MessageResources {

  public EncodeMessageResources(MessageResourcesFactory factory,
                                String config) {
    super(factory, config);
    log.info("Initializing, config='" + config + "'");
  }

  /**
   * Construct a new PropertyMessageResources according to the
   * specified parameters.
   *
   * @param factory The MessageResourcesFactory that created us
   * @param config The configuration parameter for this MessageResources
   * @param returnNull The returnNull property we should initialize with
   */
  public EncodeMessageResources(MessageResourcesFactory factory,
                                String config, boolean returnNull) {

    super(factory, config, returnNull);
    log.info("Initializing, config='" + config +
             "', returnNull=" + returnNull);

  }

  // ------------------------------------------------------------- Properties


  /**
   * The set of locale keys for which we have already loaded messages, keyed
   * by the value calculated in <code>localeKey()</code>.
   */
  protected HashMap locales = new HashMap();

  /**
   * The <code>Log</code> instance for this class.
   */
  protected static final Log log =
      LogFactory.getLog(EncodeMessageResources.class);

  /**
   * The cache of messages we have accumulated over time, keyed by the
   * value calculated in <code>messageKey()</code>.
   */
  protected HashMap messages = new HashMap();

  // --------------------------------------------------------- Public Methods


  /**
   * Returns a text message for the specified key, for the default Locale.
   * A null string result will be returned by this method if no relevant
   * message resource is found for this key or Locale, if the
   * <code>returnNull</code> property is set.  Otherwise, an appropriate
   * error message will be returned.
   * <p>
   * This method must be implemented by a concrete subclass.
   *
   * @param locale The requested message Locale, or <code>null</code>
   *  for the system default Locale
   * @param key The message key to look up
   * @return text message for the specified key and locale
   */
  public String getMessage(Locale locale, String key) {

    if (log.isDebugEnabled()) {
      log.debug("getMessage(" + locale + "," + key + ")");
    }

    // Initialize variables we will require
    String localeKey = localeKey(locale);
    String originalKey = messageKey(localeKey, key);
    String messageKey = null;
    String message = null;
    int underscore = 0;
    boolean addIt = false; // Add if not found under the original key

    // Loop from specific to general Locales looking for this message
    while (true) {

      // Load this Locale's messages if we have not done so yet
      loadLocale(localeKey);

      // Check if we have this key for the current locale key
      messageKey = messageKey(localeKey, key);
      synchronized (messages) {
        message = (String) messages.get(messageKey);
        if (message != null) {
          if (addIt) {
            messages.put(originalKey, message);
          }
          return (message);
        }
      }

      // Strip trailing modifiers to try a more general locale key
      addIt = true;
      underscore = localeKey.lastIndexOf("_");
      if (underscore < 0) {
        break;
      }
      localeKey = localeKey.substring(0, underscore);

    }

    // Try the default locale if the current locale is different
    if (!defaultLocale.equals(locale)) {
      localeKey = localeKey(defaultLocale);
      messageKey = messageKey(localeKey, key);
      loadLocale(localeKey);
      synchronized (messages) {
        message = (String) messages.get(messageKey);
        if (message != null) {
          messages.put(originalKey, message);
          return (message);
        }
      }
    }

    // As a last resort, try the default Locale
    localeKey = "";
    messageKey = messageKey(localeKey, key);
    loadLocale(localeKey);
    synchronized (messages) {
      message = (String) messages.get(messageKey);
      if (message != null) {
        messages.put(originalKey, message);
        return (message);
      }
    }

    // Return an appropriate error indication
    if (returnNull) {
      return (null);
    }
    else {
      return ("???" + messageKey(locale, key) + "???");
    }

  }

  // ------------------------------------------------------ Protected Methods


  /**
   * Load the messages associated with the specified Locale key.  For this
   * implementation, the <code>config</code> property should contain a fully
   * qualified package and resource name, separated by periods, of a series
   * of property resources to be loaded from the class loader that created
   * this PropertyMessageResources instance.  This is exactly the same name
   * format you would use when utilizing the
   * <code>java.util.PropertyResourceBundle</code> class.
   *
   * @param localeKey Locale key for the messages to be retrieved
   */
  protected void loadLocale(String localeKey) {

    if (log.isTraceEnabled()) {
      log.trace("loadLocale(" + localeKey + ")");
    }

    // Have we already attempted to load messages for this locale?
    synchronized (locales) {
      if (locales.get(localeKey) != null) {
        return;
      }
      locales.put(localeKey, localeKey);
    }

    // Set up to load the property resource for this locale key, if we can
    String name = config.replace('.', '/');
    if (localeKey.length() > 0) {
      name += "_" + localeKey;
    }
    name += ".properties";
    InputStream is = null;
    Properties props = new Properties();

    // Load the specified property resource
    try {
      if (log.isTraceEnabled()) {
        log.trace("  Loading resource '" + name + "'");
      }
      ClassLoader classLoader =
          Thread.currentThread().getContextClassLoader();
      if (classLoader == null) {
        classLoader = this.getClass().getClassLoader();
      }
      is = classLoader.getResourceAsStream(name);
      if (is != null) {
        props.load(is);
        is.close();
      }
      if (log.isTraceEnabled()) {
        log.trace("  Loading resource completed");
      }
    }
    catch (Throwable t) {
      log.error("loadLocale()", t);
      if (is != null) {
        try {
          is.close();
        }
        catch (Throwable u) {
          ;
        }
      }
    }

    // Copy the corresponding values into our cache
    if (props.size() < 1) {
      return;
    }
    synchronized (messages) {
      Enumeration names = props.keys();
      while (names.hasMoreElements()) {
        String key = (String) names.nextElement();
        if (log.isTraceEnabled()) {
          log.trace("  Saving message key '" +
                    messageKey(localeKey, key));
        }
        messages.put(messageKey(localeKey, key),
                     StringUtils.isoToGb(props.getProperty(key)));
      }
    }

  }

}
